package tripx.server;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.imageio.ImageIO;

import tripx.db.DbServiceInvoker;
import tripx.sql.SqlHandler;
import tripx.system.Logger;
import tripx.system.TripxSystem;

public abstract class Service {

	/**
	 * for database service invocation
	 */
	protected DbServiceInvoker invoker = TripxSystem.getInstance()
			.getDbServiceInvoker();

	/**
	 * this should be initialized by the subclass. provide the query action list
	 */
	protected SqlHandler sqlHandler;

	protected Logger logger = Logger.getInstance();

	/**
	 * update the database. if the action key does not match any sql, false will
	 * return. if the execution fail, false will return either.
	 * 
	 * @param actionKey
	 * @param params
	 * @return
	 */
	public int update(String actionKey, String... params) {
		logger.info("Update: " + actionKey);
		int status = 0;
		if (invoker.prepareKeyExist(actionKey)) {
			status = invoker.executeUpdateSql(actionKey, params);
		} else {
			String sql = sqlHandler.getSql(actionKey, params);
			if (sqlHandler.getStatus() == 0) {
				status = 0;
			} else {
				status = invoker.executeUpdateSql(sql);
			}
		}
		logger.info("\tGet: " + status);
		logger.info("Update Ends.");
		return status;
	}

	/**
	 * query the database. and get the json string, if there is no result, then
	 * null `[]` will return
	 * 
	 * @param actionKey
	 *            a key identify which sql to be executed
	 * @param params
	 *            if there is any parameters
	 * @return json string, if the result is empty, `[]` will return
	 */
	public String query(String actionKey, String... params) {
		logger.info("Query: " + actionKey);
		String result = "[]";
		// judge whether this action key can specify a PreparedStatement
		if (invoker.prepareKeyExist(actionKey)) {
			result = invoker.executeQuerySql(actionKey, params);
		} else {
			String sql = sqlHandler.getSql(actionKey, params);
			if (sqlHandler.getStatus() == 0) {
				result = "[]"; // empty
			} else {
				result = invoker.executeQuerySql(sql);
			}
		}
		logger.info("\tGet: " + result);
		logger.info("Query Ends");
		return result;
	}

	/**
	 * for convenience method, this method will return an Integer.
	 * 
	 * @param actionKey
	 * @param params
	 * @return
	 */
	public int queryInteger(String actionKey, String... params) {
		// String result = "[{"xid" : "12345"}]";
		logger.info("Query Integer:" + actionKey);
		String result = query(actionKey, params);
		logger.info("\tGet: " + result);
		if (result.equals("[]")) {
			return -1;
		}
		try {
			String idStr = result
					.substring(result.lastIndexOf(':') + 1,
							result.lastIndexOf('}')).replace('"', ' ').trim();
			int id = Integer.parseInt(idStr);
			return id;
		} catch (NumberFormatException e) {
			return -1;
		}
	}

	/**
	 * for convenience method, this will return a string
	 * 
	 * @param actionKey
	 * @param params
	 * @return
	 */
	public String queryString(String actionKey, String... params) {
		// the string is `[{"abc" : "abc" }]`
		String result = query(actionKey, params);
		if (result.equals("[]")) {
			return result;
		}
		return result.substring(result.indexOf(':') + 1, result.indexOf('}'))
				.trim().replace("\"", "");
	}

	/**
	 * for convenience method, this will return a list of integer
	 * 
	 * @param actionKey
	 * @param params
	 * @return
	 */
	public ArrayList<Integer> queryIntegerList(boolean canBeSame,
			String actionKey, String... params) {
		ArrayList<Integer> integerList = new ArrayList<Integer>();
		String result = query(actionKey, params);
		// if it is empty
		if (result.equals("[]")) {
			return integerList;
		}
		// String result = "[{"abc" : "12"}, {"bcd" : "23"}, {"cde" : "34"}]"
		String[] tokens = result.replaceAll("\\[", "").replaceAll("\\]", "")
				.split(",");
		for (String token : tokens) {
			int item = Integer.parseInt(token
					.substring(token.indexOf(':') + 1, token.indexOf('}'))
					.replace('"', ' ').trim());
			if (canBeSame || !integerList.contains(item)) {
				integerList.add(item);
			}
		}
		return integerList;
	}

	/**
	 * for convenience method, this will return a list of string
	 * 
	 * @param actionKey
	 * @param params
	 * @return
	 */
	public ArrayList<String> queryStringList(boolean canBeSame,
			String actionKey, String... params) {
		ArrayList<String> stringList = new ArrayList<String>();
		String result = query(actionKey, params);
		// if it is empty
		if (result.equals("[]")) {
			return stringList;
		}
		String[] tokens = result.replaceAll("\\[", "").replaceAll("\\]", "")
				.split(",");
		for (String token : tokens) {
			String item = token
					.substring(token.lastIndexOf(":") + 1,
							token.lastIndexOf("}")).trim().replaceAll("\"", "");
			if (canBeSame || !stringList.contains(item)) {
				stringList.add(item);
			}
		}
		return stringList;
	}

	/*
	 * ========================================================================
	 * Some utility methods for Image process.
	 * ========================================================================
	 */
	/**
	 * get the image byte array according the file path and the image format. if
	 * the file is not found, null will be returned.
	 * 
	 * @param path
	 * @param formatName
	 * @return
	 */
	public byte[] downloadImage(String path, String formatName) {
		try {
			BufferedImage image = ImageIO.read(new File(path));
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			ImageIO.write(image, formatName, out);
			return out.toByteArray();
		} catch (IOException e) {
			return null;
		}
	}

	/**
	 * upload image to the specified file path.
	 * 
	 * @param imageData
	 * @param imageName
	 * @param formatName
	 * @return
	 */
	public boolean uploadImage(byte[] imageData, String path, String formatName) {
		logger.info("Upload Image: " + path);
		logger.info("\tFormatName: " + formatName);
		try {
			ByteArrayInputStream in = new ByteArrayInputStream(imageData);
			BufferedImage image = ImageIO.read(in);
			File out = new File(path);
			ImageIO.write(image, formatName, out);
			logger.info("Upload End.");
			return out.exists();
		} catch (IOException e) {
			logger.err("Upload Failed.");
			return false;
		}
	}

	/**
	 * delete the specified Image.
	 * 
	 * @param imageName
	 * @return
	 */
	public boolean deleteFile(String path) {
		File file = new File(path);
		if (file.exists()) {
			return file.delete();
		}
		return true;
	}
}
